namespace SimpleWebServices.Web.UI.Html
{
    using System;
    using System.Collections;
    using System.Collections.Generic;
    using System.Text;
    using System.Xml;
    using System.Xml.Schema;
    using System.Xml.Serialization;
    using SimpleWebServices.Configuration;
    using SimpleWebServices.Web.UI.Html.Elements;

    [XmlRoot("elements")]
    public sealed class ElementCollection : IList<IElementSerializable>, IXmlSerializable
    {
        private List<IElementSerializable> _elements;
        private string _html;

        public ElementCollection()
        {
            this._elements = new List<IElementSerializable>();
        }

        public int Count
        {
            get
            {
                return this._elements.Count;
            }
        }

        public string Html
        {
            get
            {
                return this._html;
            }

            set
            {
                this._elements.Clear();
                this._html = value;
            }
        }

        public bool IsReadOnly
        {
            get
            {
                return false;
            }
        }

        public IElementSerializable this[int index]
        {
            get
            {
                return this._elements[index];
            }

            set
            {
                throw new NotSupportedException();
            }
        }

        public IElement this[string id]
        {
            get
            {
                IElement value = null;

                if (null == id)
                {
                    throw new ArgumentNullException("id");
                }

                foreach (IElementSerializable item in this)
                {
                    IElement element = item.ToElement();
                    if (id == element.Id)
                    {
                        value = element;
                    }
                    else
                    {
                        value = ElementCollection.Get(element as IInnerElements, id);
                    }

                    if (null != value)
                    {
                        break;
                    }
                }

                if (null == value)
                {
                    throw new KeyNotFoundException(id);
                }

                return value;
            }
        }

        public void Add(IElementSerializable item)
        {
            this._elements.Add(item);
            this._html = null;
        }

        public void Clear()
        {
            this._elements.Clear();
            this._html = null;
        }

        public bool Contains(IElementSerializable item)
        {
            return this._elements.Contains(item);
        }

        public void CopyTo(IElementSerializable[] array, int arrayIndex)
        {
            this._elements.CopyTo(array, arrayIndex);
        }

        public IEnumerator<IElementSerializable> GetEnumerator()
        {
            return this._elements.GetEnumerator();
        }

        IEnumerator IEnumerable.GetEnumerator()
        {
            return (this._elements as IEnumerable).GetEnumerator();
        }

        public int IndexOf(IElementSerializable item)
        {
            return this._elements.IndexOf(item);
        }

        public void Insert(int index, IElementSerializable item)
        {
            this._elements.Insert(index, item);
            this._html = null;
        }

        public void ReadXml(XmlReader reader)
        {
            if (null == reader)
            {
                throw new ArgumentNullException("reader");
            }

            if (reader.IsEmptyElement)
            {
                reader.Read();
            }
            else
            {
                string name = reader.Name;
                reader.Read();
                while (true)
                {
                    if (XmlNodeType.None == reader.NodeType)
                    {
                        break;
                    }
                    else if (XmlNodeType.EndElement == reader.NodeType && reader.Name == name)
                    {
                        reader.Read();
                        break;
                    }
                    else if (XmlNodeType.Text == reader.NodeType)
                    {
                        this.Add(new TextString(reader.ReadContentAsString()));
                    }
                    else
                    {
                        this.Add(HtmlElementConfiguration.Deserialize(reader.Name, reader.GetAttribute("type"), reader.ReadOuterXml()));
                    }
                }
            }
        }

        public bool Remove(IElementSerializable item)
        {
            bool result = this._elements.Remove(item);
            this._html = null;

            return result;
        }

        public void RemoveAt(int index)
        {
            this._elements.RemoveAt(index);
            this._html = null;
        }

        public string Render()
        {
            StringBuilder buffer = new StringBuilder();

            foreach (IElementSerializable element in this)
            {
                buffer.Append(element.Render());
            }

            return buffer.ToString();
        }

        public override string ToString()
        {
            return this.Render();
        }

        XmlSchema IXmlSerializable.GetSchema()
        {
            throw new NotSupportedException();
        }

        void IXmlSerializable.ReadXml(XmlReader reader)
        {
            this.ReadXml(reader);
        }

        void IXmlSerializable.WriteXml(XmlWriter writer)
        {
            if (null == writer)
            {
                throw new ArgumentNullException("writer");
            }

            foreach (IElement element in this)
            {
                writer.WriteRaw(element.ToString());
            }
        }

        private static IElement Get(IInnerElements inner, Name id)
        {
            IElement result = null;

            if (null != inner)
            {
                foreach (IElementSerializable item in inner.Elements)
                {
                    IElement element = item.ToElement();
                    if (id == element.Id)
                    {
                        result = element;
                    }
                    else
                    {
                        result = ElementCollection.Get(element as IInnerElements, id);
                    }

                    if (null != result)
                    {
                        break;
                    }
                }
            }

            return result;
        }
    }
}